package queue

import (
	"runtime"
	"sync/atomic"
)

/*
 * 使用读写游标判断
 * ReadWriteQueue is a one producer and one consumer queue
 * without locks.
 */

type ReadWriteQueue struct {
	//_padding0  [64]byte
	size       uint32
	_padding1  [64]byte
	records    []interface{}
	_padding2  [64]byte
	readIndex  uint32
	_padding3  [64]byte
	writeIndex uint32
}

// capaciity must be >= 2

func NewReadWriteQueue(capaciity uint32) *ReadWriteQueue {
	q := new(ReadWriteQueue)
	q.size = capaciity
	q.records = make([]interface{}, q.size)
	q.readIndex = 0
	q.writeIndex = 0
	return q
}

func (m *ReadWriteQueue) Write(value interface{}) bool {
	currentWrite := atomic.LoadUint32(&m.writeIndex)
	nextRecord := currentWrite + 1
	if nextRecord == m.size {
		nextRecord = 0
	}

	if nextRecord == atomic.LoadUint32(&m.readIndex) {
		// queue is full
		runtime.Gosched()
		return false
	}

	m.records[currentWrite] = value
	atomic.StoreUint32(&m.writeIndex, nextRecord)
	return true
}

func (m *ReadWriteQueue) Read() (interface{}, bool) {
	currentRead := atomic.LoadUint32(&m.readIndex)
	if currentRead == atomic.LoadUint32(&m.writeIndex) {
		// queue is empty
		runtime.Gosched()
		return nil, false
	}

	nextRecord := currentRead + 1
	if nextRecord == m.size {
		nextRecord = 0
	}

	out := m.records[currentRead]
	m.records[currentRead] = nil
	atomic.StoreUint32(&m.readIndex, nextRecord)
	return out, true
}

func (m *ReadWriteQueue) Front() (interface{}, bool) {
	currentRead := atomic.LoadUint32(&m.readIndex)
	if currentRead == atomic.LoadUint32(&m.writeIndex) {
		// queue is empty
		return nil, false
	}
	return m.records[currentRead], true
}

// queue must not be empty
func (m *ReadWriteQueue) popFront() {
	currentRead := atomic.LoadUint32(&m.readIndex)
	if currentRead == atomic.LoadUint32(&m.writeIndex) {
		return
	}

	nextRecord := currentRead + 1
	if nextRecord == m.size {
		nextRecord = 0
	}

	m.records[currentRead] = nil
	atomic.StoreUint32(&m.readIndex, nextRecord)
}

func (m *ReadWriteQueue) IsEmpty() bool {
	return atomic.LoadUint32(&m.readIndex) == atomic.LoadUint32(&m.writeIndex)
}

func (m *ReadWriteQueue) IsFull() bool {
	nextRecord := atomic.LoadUint32(&m.writeIndex) + 1
	if nextRecord == m.size {
		nextRecord = 0
	}

	if nextRecord != atomic.LoadUint32(&m.readIndex) {
		return false
	}
	// queue is full
	return true
}

// maximum number of items in the queue.
func (m *ReadWriteQueue) Capacity() uint32 {
	return m.size - 1
}
